#include "ImageObj.h"

ImageObj::ImageObj(void)
{
		fileSize = 0;
		beginData = 0;
		sizeHeader = 0;
		width = 0;
		height = 0;
		colorPlane = 0;
		bitByPixel = 0;
		compression = 0;
		sizeData = 0;
		horizontalResolution = 0;
		verticalResolution = 0;
		colorByPalette = 0;
		importantColor = 0;
		paddingBytes = 0;
		selectRectangle = new long[4];
}
RGBColor** ImageObj::DCT8(RGBColor** part) {//NLE
	//init C
	//cout << "DCT8" << endl;
	double** C = new double*[8];
	double** Ct = new double*[8];
	for (long i=0;i<8;i++) {
		C[i] = new double[8];
		Ct[i] = new double[8];
	}
	//cout << "CetCt" << endl;
	for (long i=0;i<8;i++) {
		for (long j=0;j<8;j++) {
			//cout << "("<<i<<","<<j<<")" << endl;
			if (i==0) {
				C[i][j]=1.0/sqrt(8.0);
				Ct[j][i]=1.0/sqrt(8.0);
			} else {
				C[i][j]=sqrt(2.0/8.0)*cos(((2.0*j+1.0)*i*PI)/16.0);
				Ct[j][i]=sqrt(2.0/8.0)*cos(((2.0*j+1.0)*i*PI)/16.0);
			}
		}
	}
	//cout << "matrixX" << endl;
	// multiplication matricielle C*part*Ct
	RGBColor** Tpart = part;
	RGBColor** DCTpart = part;

	for (long i=0;i<8;i++) {
		for (long j=0;j<8;j++) {
			//cout << "("<<i<<","<<j<<")" << endl;
			double r=0;
			double g=0;
			double b=0;
			for (long k=0;k<8;k++) {
				r+= C[i][k]*(part[k][j].red);
				g+= C[i][k]*(part[k][j].green);
				b+= C[i][k]*(part[k][j].blue);
			}
			Tpart[i][j].red = r;
			Tpart[i][j].green = g;
			Tpart[i][j].blue = b;
		}
	}
	//cout << "matrixX2" << endl;
	for (long i=0;i<8;i++) {
		for (long j=0;j<8;j++) {
			double r=0;
			double g=0;
			double b=0;
			for (long k=0;k<8;k++) {
				r+= (Tpart[i][k].red)*Ct[k][j];
				g+= (Tpart[i][k].green)*Ct[k][j];
				b+= (Tpart[i][k].blue)*Ct[k][j];
			}
			//cout << "[" <<i<<","<<j<<"]=("<<r<<","<<g<<","<<b<<")"<<endl ;
			DCTpart[i][j].red = r;
			DCTpart[i][j].green = g;
			DCTpart[i][j].blue = b;
		}
	}

	return DCTpart;
}
RGBColor** ImageObj::compositeDCT() {//NLE
	cout << width << "x" << height << endl;
	RGBColor** dct = new RGBColor*[height];
	RGBColor** part = new RGBColor*[8];
	for(long i = 0; i < height; i++)
	{
		dct[i] = new RGBColor[width];
		for(long j = 0; j < width; j++)
		{
			RGBColor* couleur = new RGBColor();
			dct[i][j] = *couleur;
		}
	}
	for(long i = 0; i < 8; i++)
	{
		part[i] = new RGBColor[width];
		for(long j = 0; j < 8; j++)
		{
			RGBColor* couleur = new RGBColor();
			part[i][j] = *couleur;
		}
	}
	long i=0;
	long j=0;
	RGBColor* blank =  new RGBColor((byte)0,(byte)0,(byte)0);
	//cout<<"HxW:"<<height<<"x"<<width<<endl;
	while (i<height) { //NLE-D
		while (j<width) { //NLE-D
			// mettre bloc 8*8 dans part
			for (long k=0;k<8;k++) {
				for (long l=0;l<8;l++) {
					if (((i+k)<height)&&((j+l)<width)) {
						part[k][l]=image[i+k][j+l];
					} else {
						part[k][l]= *blank;
					}
				}
			}
			part = DCT8(part);
			// mettre part dans dct
			for (long k=0;k<8;k++) {
				for (long l=0;l<8;l++) {
					if (((i+k)<height)&&((j+l)<width)) {
						dct[i+k][j+l]=part[k][l];
					}
				}
			}
			j=j+8;
		}
		i=i+8;
		j=0;
	}
	return dct;
}
ImageObj::ImageObj(ulong _fileSize, ulong _beginData, ulong _sizeHeader, ulong _width, ulong _height, ulong _colorPlane,
				   ulong _bitByPixel, ulong _compression, ulong _sizeData, ulong _horizontalResolution,
				   ulong _verticalResolution, ulong _colorByPalette, ulong _importantColor, RGBColor** _image)
{
		fileSize = _fileSize;
		beginData = _beginData;
		sizeHeader = _sizeHeader;
		width = _width;
		height = _height;
		colorPlane = _colorPlane;
		bitByPixel = _bitByPixel;
		compression = _compression;
		sizeData = _sizeData;
		horizontalResolution = _horizontalResolution;
		verticalResolution = _verticalResolution;
		colorByPalette = _colorByPalette;
		importantColor = _importantColor;
		selectRectangle = new long[4];
		
		image = new RGBColor*[height];
		for(long i = 0; i < height; i++)
		{
			image[i] = new RGBColor[width];
			for(long j = 0; j < width; j++)
			{
				image[i][j] = _image[i][j]; 
			}
		}
}


ImageObj::ImageObj(long width, long height)
{
	fileSize = 0;
	beginData = 0;
	sizeHeader = 0;
	width = width;
	height = height;
	colorPlane = 0;
	bitByPixel = 0;
	compression = 0;
	sizeData = 0;
	horizontalResolution = 0;
	verticalResolution = 0;
	colorByPalette = 0;
	importantColor = 0;
	paddingBytes = 0;
	selectRectangle = new long[4];

	image = new RGBColor*[height];
	for(long i = 0; i < height; i++)
	{
		image[i] = new RGBColor[width];
		for(long j = 0; j < width; j++)
		{
			RGBColor* couleur = new RGBColor();
			image[i][j] = *couleur; 
		}
	}
}

void ImageObj::createImage(long width, long height)
{
	image = new RGBColor*[height];
	for(long i = 0; i < height; i++)
	{
		image[i] = new RGBColor[width];
		for(long j = 0; j < width; j++)
		{
			RGBColor* couleur = new RGBColor();
			image[i][j] = *couleur; 
		}
	}
}


void ImageObj::cropImage(ulong _width, ulong _height, RGBColor** _image)
{
		fileSize = fileSize - sizeData + _width*_height*(bitByPixel/8);// - (width - _width)*(bitByPixel/8) - (height - _height)*(bitByPixel/8);
		width = _width;
		height = _height;
		sizeData = _width*_height*(bitByPixel/8);

		image = new RGBColor*[_height];
		for(long i = 0; i < _height; i++)
		{
			image[i] = new RGBColor[_width];
			for(long j = 0; j < _width; j++)
			{
				image[i][j] = _image[i][j]; 
			}
		}
}


void ImageObj::openImage(const char *file)
{
	ifstream img;
	format = new char[3];
	format[2] = '\0';

	img.open(file, ios::binary);
	if(img.good())
	{
		img.read(this->format, 2);
		if (strcmp(this->format, "BM") == 0)
		{
			//img.seekg(2,ios::beg);
			img.read((char *)&fileSize, 4);
			img.seekg(10,ios::beg);
			img.read((char *)&beginData,4);
			img.read((char *)&sizeHeader,4);
			img.read((char *)&width,4);
			img.read((char *)&height,4);
			img.read((char *)&colorPlane,2);
			img.read((char *)&bitByPixel,2);
			if (this->bitByPixel != 24 && this->bitByPixel != 32) throw 3;
			img.read((char *)&compression,4);
			if (this->compression != 0) throw 2;
			img.read((char *)&sizeData,4);
			img.read((char *)&horizontalResolution,4);
			img.read((char *)&verticalResolution,4);
			img.read((char *)&colorByPalette,4);
			img.read((char *)&importantColor,4);
			this->createImage(width,height);

			if((width*(bitByPixel/8))%4 == 0) paddingBytes = 0;
			else paddingBytes = 4 - (width*(bitByPixel/8))%4;
			//img.seekg(beginData,ios::beg);
			
			for(long i = 0; i < height; i++)
			{
				for(long j = 0; j < width; j++)
				{	
					img.read((char *)&image[i][j].blue,1);
					img.read((char *)&image[i][j].green,1);
					img.read((char *)&image[i][j].red,1);
					if(bitByPixel == 32) img.read((char *)&image[i][j].alpha,1);
				}
				img.seekg(paddingBytes,ios::cur);

			}
			img.close();
		}
		else
		{
			img.close();
			throw 0;
		}
		
	}
	else
	{
		throw 1;
	}
}


void ImageObj::writeImage(const char *file)
{
	ofstream writtenFile;
	writtenFile.open(file, ios::out | ios::binary);
	
	if (writtenFile.is_open())
	{

		if((width*(bitByPixel/8))%4 == 0) paddingBytes = 0;
		else paddingBytes = 4 - (width*(bitByPixel/8))%4;

		if(paddingBytes != 0)
		{
			fileSize = fileSize + height*paddingBytes + 2;
			sizeData = sizeData + height*paddingBytes + 2;
		}

		writtenFile.write("BM", 2);
		writtenFile.write((char *)&fileSize, 4);
		writtenFile.write("\0\0\0\0", 4);
		writtenFile.write((char *)&beginData, 4);
		writtenFile.write((char *)&sizeHeader, 4);
		writtenFile.write((char *)&width, 4);
		writtenFile.write((char *)&height, 4);
		writtenFile.write((char *)&colorPlane, 2);
		writtenFile.write((char *)&bitByPixel, 2);
		writtenFile.write((char *)&compression, 4);
		writtenFile.write((char *)&sizeData, 4);
		writtenFile.write((char *)&horizontalResolution, 4);
		writtenFile.write((char *)&verticalResolution, 4);
		writtenFile.write((char *)&colorByPalette, 4);
		writtenFile.write((char *)&importantColor, 4);

		for(long i = 0; i < height; i++)
		{
			for(long j = 0; j < width; j++)
			{
				writtenFile.write((char *)&image[i][j].blue,1);
				writtenFile.write((char *)&image[i][j].green,1);
				writtenFile.write((char *)&image[i][j].red,1);
				if(bitByPixel == 32) writtenFile.write((char *)&image[i][j].alpha,1);

			}
			for(long j = width; j < width + paddingBytes; j++)
			{
				writtenFile.write("\0",1);
			}
		}
		if(paddingBytes != 0) writtenFile.write((char *)&paddingBytes,2);

	}
	writtenFile.close();
}

bool ImageObj::contains(long x, long y)
{
	if(x > 0 && y > 0)
		if(x < width && y < height) return true;
	return false;
}

void ImageObj::cropRectangle(long cropX, long cropWidth,long cropY,long cropHeight)
{
	long widthContainedByImage = 0;
	long heightContainedByImage = 0;

	if(cropX <= 0)
	{
		cropWidth = cropWidth + cropX;
		cropX = 0;
	}
	if(cropY <= 0)
	{
		cropHeight = cropHeight + cropY;
		cropY = 0;
	}

	//Calcul de la largeur a croper
	if(cropX + cropWidth <= width) widthContainedByImage = cropWidth;
	if(cropX + cropWidth > width) widthContainedByImage = width - cropX;
		
	//Calcul de la hauteur a croper
	if(cropY + cropHeight <= height) heightContainedByImage = cropHeight;
	if(cropY + cropHeight > height) heightContainedByImage = height - cropY;

	selectRectangle[0] = cropX;
	selectRectangle[1] = cropY;
	selectRectangle[2] = widthContainedByImage;
	selectRectangle[3] = heightContainedByImage;
}

long ImageObj::getCropX()
{
	return selectRectangle[0];
}
long ImageObj::getCropY()
{
	return selectRectangle[1];
}
long ImageObj::getCropWidth()
{
	return selectRectangle[2];
}
long ImageObj::getCropHeight()
{
	return selectRectangle[3];
}

ImageObj& ImageObj::operator=(const ImageObj& that)
{
	if(this == &that)
	{
		return *this;
	}
	fileSize = that.fileSize;
	beginData = that.beginData;
	sizeHeader = that.sizeHeader;
	width = that.width;
	height = that.height;
	colorPlane = that.colorPlane;
	bitByPixel = that.bitByPixel;
	compression = that.compression;
	sizeData = that.sizeData;
	horizontalResolution = that.horizontalResolution;
	verticalResolution = that.verticalResolution;
	colorByPalette = that.colorByPalette;
	importantColor = that.importantColor;
	
	image = new RGBColor*[height];
	for(long i = 0; i < height; i++)
	{
		image[i] = new RGBColor[width];
		for(long j = 0; j < width; j++)
		{
			image[i][j] = that.image[i][j];
		}
	}
	return *this;
}

ImageObj::~ImageObj()
{
	fileSize = 0;	
	width = 0;
	height = 0;
	sizeData = 0;
	beginData = 0;
	for(long i = 0; i < height; i++)
	{
		delete image[i];
	}
	delete image;
}

